新一代微服务基础设施servie mesh(2) - Istio流量管理

  前文我们提到过Service mesh分为control plane和data plane两个平面,data plane负责服务间的通信管理,而control plane负责策略配置收集监控数据鉴权等事项。严格意义来讲Istio只具有控制平面的功能,而在Istio中的data plane的功能是使用另外一个开源项目envoy来时实现的。这里为了不混淆,下文中的Istio只包括其control plane,而data plane使用envoy进行专指。前文已对Istio做完基本的介绍,按照正常的套路就应该介绍Istio的安装,但因为Istio的安装很简单,这里就不再赘述,大家可以移步https://istio.io/docs/setup/kubernetes/quick-start/。不过我们在安装的时候遇到几个问题可以在这里需要提一下:

  • 安装istio时建议测试环境和没有外部业务通信的场景下建议不启动auth组件;
  • 安装sidecar可以选择使用istioctl 命令行工具手动将sidecar注入到应用所在的pod,或者使用Istio Initializer每次在创建pod的时候自动将sidecar注入到业务容器所在那个pod里面
  • 由于我们的k8s环境不支持loadbalance类型的TYPE,而istio-ingressgateway默认使用这个类型,导致在启动的时候pod都一直处于Pending状态,后来手动将类型改为nodeport,就可以正常启动。

Istio组件介绍

  说完Istio的整体,那我们再看看看Istio的各个组件,先上一张官方文档里面的图:

http://philcalcado.com/2017/08/03/pattern_service_mesh.html

图片来自https://istio.io/docs/concepts/what-is-istio/overview/

  可以看到Istio在控制平面实际上有三个较为关键的组件。mixer主要有功能有:1、转发前的check(黑白名单、quota等)、转发后上报数据(监控)。这里说个题外话,目前mixer是整个Istio的一个性能瓶颈点之一,比如一次mesh服务间的网络通信至少需要两次和mixer进行RPC通信(调用前的check和调用后的report),所以mixer后续会是我们在定制化的时候需要做大量的优化,关于mixer这里暂时先按下不表,咱们后文详细说。第三个citadel主要适用于安全证书和鉴权方面。
pilot主要是负责配置通信控制策略并下发给envoy组件,那到底有哪些策略可以配置呢,主要就是提供服务发现与注册服务治理、动态的更新负载均衡的后端服务实例(定时监测某个服务是否存活)、更新route table(例如根据http的后缀进行匹配以确定具体发送给服务的某个版本)以及管理访问Service mesh内部业务的流入流量(ingress)和service mesh内部访问外部服务的流出的流量(egress)。

istio中的请求路由

  istio的提供了所谓的服务版本的概念,通过将流量分发到同一服务的不同版本(或者不同的配置环境,例如:生产、测试等)就可以实现一个通用的流量路由控制场景,比如A/B测试等。当然也可以通过服务版本的概念实现负载均衡的分发策略。通过服务版本的模型,就可以实现业务与和他所依赖服务的解耦。

  Istio的流量路由配置是通过提供一套API来进行路由规则的配置,目前有3个版本的API。在Istio0.7.X版本支持的事config.istio.io/v1alpha2版本。在最近的0.8.0版本开始支持config.istio.io/v1alpha3版本API。v1alpha3版本的API对v1alpha2中的API进行重构,也提供了更为灵活和强大的路由配置规则来实现在4层和7层网络的的流量控制和高级别服务治理如:熔断、降级、超时。同时也提供了一些通用的持续发布能力,例如金丝雀发布、灰度发布、A/B test
  下来我会顺着官方的文档进行介绍,会加入一些对此的理解。
  我们先上一个完整的路由配置策略,一下这个配置示例是实现将流入的流量100%的分发给一个叫做reviews的V1版本。

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1

  以上配置表明所有访问reviews服务(通过hosts字段进行配置)的流量都会被路由到reviews服务下面的V1版本的具体实例。那么关于reviews服务具有多少个路由子集(subset)是在对应的同名DestinationRule资源对象中进行配置的。

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2

  在subset配置中一般包含某个服务的一个或几个指定版本的服务实例,例如在使用kubernetes的deployment的资源对象进行部署的时候,这里的labels标签实际上就是和pod的label标签是等价的,例如在DestinationRule.subsets.labels=” version: v1”表示只有标注有version:v1这个label的pod会被导入请求流量。
  在istio的v1alpha3版本API中有4个配置资源对象,分别是VirtualService, DestinationRule, ServiceEntry和Gateway。

virtualService配置资源对象

virtualService表示一个对service mesh中的服务如何进行路由的资源配置对象。对于一个服务的请求,virtualService可以根据其请求源和请求目标或者根据HTTP的URL路径或者报文头的字段匹配来决定将其路由到一个服务的不同版本,甚至另外一个完全不同的服务上。

目标服务

一个路由规则需要定义至少一个请求的最终目标服务,目标服务的配置就是在virtualService中的hosts字段,定义的目标服务也不需要和所映射的服务名保持名称一致,甚至在host中定义的目标服务后端不存在一个和名称对应的真实Service mesh 中的服务。在host中定义的服务名会被转义成kubernetes中的FQDN( fully qualified domain names),例如hosts中配置名为reviews的服务,就会被扩展成reviews.default.svc.cluster.local

根据请求源进行路由规则匹配

  1. 根据请求源服务来进行路由规则匹配,也就是说指定只有某个特定的服务来请求目标服务时,才会被路由规则匹配并且将请求转发给指定目标服务,例如以下配置:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
    name: ratings
    spec:
    hosts:
    - ratings
    http:
    - match:
    sourceLabels:
    app: reviews
    ...

这里的sourcelaLabels属性对应的值就是限制请求源服务的,在k8s里面就是需要请求源服务被设置了 app: reviews这个值
2、 限制源服务除了指定具体的服务明以外,还可以更细粒度也就是服务的版本,比如下面配置表示,只有reviews这个mesh服务的V2版本才能匹配成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- match:
- sourceLabels:
app: reviews
version: v2
...

3、 还可以通过请求的HTTP报文头里面的某个属性字段来进行匹配,甚至可以支持正则表达式和一次设置多个表达式。这里就不再一一举例,详情请各位看官参照文档

根据服务的版本分发流量

每个路由规则都会定义一个或多个被规则引入流量的目标服务,目标服务对应会有版本,版本号是通过label标签来进行标识的,若一个服务的某一个版本有多个注册实例,则按照设置好的轮询算法或者使用默认的round-robin算法进行负责均衡,负载均衡的流量比例通过weight字段来标识,例如下列配置demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 75
- destination:
host: reviews
subset: v2
weight: 25

超时和重试

在Istio中的默认超时时间是15s,但是可以通过定义timeout属性来重写超时时间,如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- route:
- destination:
host: ratings
subset: v1
retries:
attempts: 3
perTryTimeout: 2s

重试次数也可以通过配置实现,配置的时候需要给定两个值,重试的最大重试次数和每次重试的超时时间,如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- route:
- destination:
host: ratings
subset: v1
retries:
attempts: 3
perTryTimeout: 2s

在请求中注入错误

在Istio中注入的错误类型可以分为两种类型:超时和终止。以下配置表示有10%的访问ratings服务V1版本的请求将会有至少5s的延时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- fault:
delay:
percent: 10
fixedDelay: 5s
route:
- destination:
host: ratings
subset: v1

在另外一种注入错误:终止。可以用来提起终止一个请求,例如以下配置表示有10%的访问rating服务V1版本的请求将会返回HTTP400错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- fault:
abort:
percent: 10
httpStatus: 400
route:
- destination:
host: ratings
subset: v1

超时与提前终止请求可以同时使用,以下配置将使所有请求都延迟5s,并且其中的10%直接返回400错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- match:
- sourceLabels:
app: reviews
version: v2
fault:
delay:
fixedDelay: 5s
abort:
percent: 10
httpStatus: 400
route:
- destination:
host: ratings
subset: v1

HTTP路由规则的优先级顺序

当对于目标服务有多个规则是,则按照他们在virtualService中出现的顺序进行优先级排序,在编写请求路由时,有一个路由编写规范就是match匹配请求源和请求报文头放到最前面以设置最高优先级,然后再单独提供基于权重但是没有匹配表达式的weight规则来匹配剩余的请求流量。例如对于下面的配置中的virtualService有两条对应的目标规则。指定了所有的请求如果报文头中包含Foo字段并且其值为bar则将流量发送到V2版本实例。剩余的请求流量将路由至V1版本实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- match:
- headers:
Foo:
exact: bar
route:
- destination:
host: reviews
subset: v2
- route:
- destination:
host: reviews
subset: v1

Destination Rules配置资源对象

Destination Rules配置了一系列的规则用于在virtualService已经匹配成功后的后续路由动作,主要用于描述熔断器、负载均衡策略,TLS设置等等相关配置。destinationRules会定义好一个目标服务对应的的子集(subset)。这个subset将根据VirtualService匹配后的请求路由到制定的某个服务的具体版本上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
trafficPolicy:
loadBalancer:
simple: RANDOM
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
- name: v3
labels:
version: v3

host层面已经指定了默认的负载均衡策略,但在V2服务版本的配置重写了默认的负载均衡策略,这样当V2版本有多个实例时,则可以根据这个重写的规则来确定具体的负载均衡分发策略

熔断器

一个简单的熔断器配置可以根据连接和请求的流量最大值进行限流,例如以下配置中,在DestinationRule设置了reviews服务最大可以建立100个连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100

Service Entries配置资源对象

ServiceEntry允许将外部服务注册到istio内部维护的注册表里,这个主要就是为了service mesh内部访问外部的服务。例如以下配置就是将在*.abc.com域名下的外部服务声明为一个服务,共service mesh内部访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: foo-ext-svc
spec:
hosts:
- *.foo.com
ports:
- number: 80
name: http
protocol: HTTP
- number: 443
name: https
protocol: HTTPS

ServiceEntry分为内部和外部两种类型,内部服务和其他service mesh服务没有什么区别,只是为了显示的声明将某个服务注册到service mesh中。例如将某个私有云内部部署在虚拟机上面的业务显式地添加到基于k8s的service mesh服务当中。外部类型就主要表示外部服务,此类外部服务无法使用TLS进行通信,并且很多策略的控制都是在客户端进行的,而不是像在内部服务请求一样在服务端执行策略。

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: bar-foo-ext-svc
spec:
hosts:
- bar.foo.com
http:
- route:
- destination:
host: bar.foo.com
timeout: 10s

gateway配置资源对象

gateway为HTTP/TCP流量配置负载均衡器,最常见的是在service meshb边缘运行,以启用控制流入service mesh的流量。与Kubernetes Ingress不同,Istio Gateway仅配置L4-L6功能(例如,要暴露的端口,TLS配置)。用户可以使用标准的Istio规则来控制HTTP请求以及通过将VirtualService绑定到网关而进入网关的TCP流量。例如,以下简单网关配置负载均衡器,以允许主机bookinfo.com的外部https流量进入service mesh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: bookinfo-gateway
spec:
servers:
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- bookinfo.com
tls:
mode: SIMPLE
serverCertificate: /tmp/tls.crt
privateKey: /tmp/tls.key

如果要映射路由到后端的具体某个服务还需要配置相应的路由,virtualService必须使用配置gateway定义的为同一host配置VirtualService:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: bookinfo
spec:
hosts:
- bookinfo.com
gateways:
- bookinfo-gateway # <---- bind to gateway
http:
- match:
- uri:
prefix: /reviews
route:
...

-------------本文到此结束,感谢您的阅读-------------